package com.hung.tripleregistry.service.impl;

import com.hung.triple.common.exception.TripleRpcException;
import com.hung.tripleregistry.core.model.*;
import com.hung.tripleregistry.core.result.ReturnT;
import com.hung.tripleregistry.core.util.PropUtil;
import com.hung.tripleregistry.dao.RpcRegistryDataMapper;
import com.hung.tripleregistry.dao.RpcRegistryMapper;
import com.hung.tripleregistry.dao.RpcRegistryMessageMapper;
import com.hung.tripleregistry.service.RpcRegistryService;
import com.hung.tripleregistry.util.GsonTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;

/**
 * @author Hung
 * @date 2022/10/3 21:34
 */
@Service
@Slf4j
public class RpcRegistryServiceImpl implements RpcRegistryService, InitializingBean, DisposableBean {

    @Resource
    private RpcRegistryMapper rpcRegistryMapper;
    @Resource
    private RpcRegistryDataMapper rpcRegistryDataMapper;
    @Resource
    private RpcRegistryMessageMapper rpcRegistryMessageMapper;

    @Value("${rpc.registry.data.filepath}")
    private String registryDataFilePath;
    @Value("${rpc.registry.accessToken}")
    private String accessToken;

    private int registryBeatTime = 10;
    private ExecutorService executorService = Executors.newCachedThreadPool();
    private volatile boolean executorStoped = false;
    private volatile List<Integer> readedMessageIds = Collections.synchronizedList(new ArrayList<>());
    private volatile LinkedBlockingQueue<RpcRegistryData> registryQueue = new LinkedBlockingQueue<>();
    private volatile LinkedBlockingQueue<RpcRegistryData> removeQueue = new LinkedBlockingQueue<>();


    // ------------------------ remote registry ------------------------
    private Map<String, List<DeferredResult>> registryDeferredResultMap = new ConcurrentHashMap<>();

    @Override
    public Map<String, Object> pageList(int start, int length, String env, String key) {

        // page list
        List<RpcRegistry> list = rpcRegistryMapper.pageList(start, length, env, key);
        int list_count = rpcRegistryMapper.pageListCount(start, length, env, key);

        // package result
        Map<String, Object> maps = new HashMap<>();
        maps.put("recordsTotal", list_count);        // 总记录数
        maps.put("recordsFiltered", list_count);    // 过滤后的总记录数
        maps.put("data", list);                    // 分页列表
        return maps;
    }

    @Override
    public ReturnT<String> delete(int id) {
        RpcRegistry rpcRegistry = rpcRegistryMapper.loadById(id);
        if (rpcRegistry != null) {
            rpcRegistryMapper.delete(id);
            rpcRegistryDataMapper.deleteData(rpcRegistry.getEnv(), rpcRegistry.getKey());

            // sendRegistryDataUpdateMessage (delete)
            rpcRegistry.setData("");
            sendRegistryDataUpdateMessage(rpcRegistry);
        }

        return ReturnT.SUCCESS;
    }

    /**
     * send RegistryData Update Message
     */
    private void sendRegistryDataUpdateMessage(RpcRegistry rpcRegistry) {
        String registryUpdateJson = GsonTool.toJson(rpcRegistry);

        RpcRegistryMessage registryMessage = new RpcRegistryMessage();
        registryMessage.setType(0);
        registryMessage.setData(registryUpdateJson);
        rpcRegistryMessageMapper.add(registryMessage);
    }

    @Override
    public ReturnT<String> update(RpcRegistry rpcRegistry) {

        // valid
        if (rpcRegistry.getEnv() == null || rpcRegistry.getEnv().trim().length() < 2 || rpcRegistry.getEnv().trim().length() > 255) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "环境格式非法[2~255]");
        }
        if (rpcRegistry.getKey() == null || rpcRegistry.getKey().trim().length() < 4 || rpcRegistry.getKey().trim().length() > 255) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "注册Key格式非法[4~255]");
        }
        if (rpcRegistry.getData() == null || rpcRegistry.getData().trim().length() == 0) {
            rpcRegistry.setData(GsonTool.toJson(new ArrayList<String>()));
        }
        List<String> valueList = GsonTool.fromJson(rpcRegistry.getData(), List.class);
        if (valueList == null) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "注册Value数据格式非法；限制为字符串数组JSON格式，如 [address,address2]");
        }

        // valid exist
        RpcRegistry exist = rpcRegistryMapper.loadById(rpcRegistry.getId());
        if (exist == null) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "ID参数非法");
        }

        // if refresh
        boolean needMessage = !rpcRegistry.getData().equals(exist.getData());

        int ret = rpcRegistryMapper.update(rpcRegistry);
        needMessage = ret > 0 && needMessage;

        if (needMessage) {
            // sendRegistryDataUpdateMessage (update)
            sendRegistryDataUpdateMessage(rpcRegistry);
        }

        return ret > 0 ? ReturnT.SUCCESS : ReturnT.FAIL;
    }

    // ------------------------ broadcase + file data ------------------------

    @Override
    public ReturnT<String> add(RpcRegistry rpcRegistry) {

        // valid
        if (rpcRegistry.getEnv() == null || rpcRegistry.getEnv().trim().length() < 2 || rpcRegistry.getEnv().trim().length() > 255) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "环境格式非法[2~255]");
        }
        if (rpcRegistry.getKey() == null || rpcRegistry.getKey().trim().length() < 4 || rpcRegistry.getKey().trim().length() > 255) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "注册Key格式非法[4~255]");
        }
        if (rpcRegistry.getData() == null || rpcRegistry.getData().trim().length() == 0) {
            rpcRegistry.setData(GsonTool.toJson(new ArrayList<String>()));
        }
        List<String> valueList = GsonTool.fromJson(rpcRegistry.getData(), List.class);
        if (valueList == null) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "注册Value数据格式非法；限制为字符串数组JSON格式，如 [address,address2]");
        }

        // valid exist
        RpcRegistry exist = rpcRegistryMapper.load(rpcRegistry.getEnv(), rpcRegistry.getKey());
        if (exist != null) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "注册Key请勿重复");
        }

        int ret = rpcRegistryMapper.add(rpcRegistry);
        boolean needMessage = ret > 0;

        if (needMessage) {
            // sendRegistryDataUpdateMessage (add)
            sendRegistryDataUpdateMessage(rpcRegistry);
        }

        return ret > 0 ? ReturnT.SUCCESS : ReturnT.FAIL;
    }

    @Override
    public RpcAdminRegistryResponse registry(RpcAdminRegistryRequest registryRequest) {

        // valid
        if (this.accessToken != null && this.accessToken.trim().length() > 0 && !this.accessToken.equals(registryRequest.getAccessToken())) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "AccessToken Invalid");
        }
        if (registryRequest.getEnv() == null || registryRequest.getEnv().trim().length() < 2 || registryRequest.getEnv().trim().length() > 255) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Env Invalid[2~255]");
        }
        if (registryRequest.getRegistryDataList() == null || registryRequest.getRegistryDataList().size() == 0) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Registry DataList Invalid");
        }

        for (RpcAdminRegistryDataItem registryData : registryRequest.getRegistryDataList()) {
            if (registryData.getKey() == null || registryData.getKey().trim().length() < 4 || registryData.getKey().trim().length() > 255) {
                return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Registry Key Invalid[4~255]");
            }
            if (registryData.getValue() == null || registryData.getValue().trim().length() < 4 || registryData.getValue().trim().length() > 255) {
                return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Registry Value Invalid[4~255]");
            }
        }

        // fill + add queue
        List<RpcRegistryData> registryDataList = new ArrayList<>();
        for (RpcAdminRegistryDataItem dataItem : registryRequest.getRegistryDataList()) {
            RpcRegistryData registryData = new RpcRegistryData();
            registryData.setEnv(registryRequest.getEnv());
            registryData.setKey(dataItem.getKey());
            registryData.setValue(dataItem.getValue());

            registryDataList.add(registryData);
        }
        registryQueue.addAll(registryDataList);

        return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.SUCCESS_CODE, null);
    }

    @Override
    public RpcAdminRegistryResponse remove(RpcAdminRegistryRequest registryRequest) {

        // valid
        if (this.accessToken != null && this.accessToken.trim().length() > 0 && !this.accessToken.equals(registryRequest.getAccessToken())) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "AccessToken Invalid");
        }
        if (registryRequest.getEnv() == null || registryRequest.getEnv().trim().length() < 2 || registryRequest.getEnv().trim().length() > 255) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Env Invalid[2~255]");
        }
        if (registryRequest.getRegistryDataList() == null || registryRequest.getRegistryDataList().size() == 0) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Registry DataList Invalid");
        }
        for (RpcAdminRegistryDataItem registryData : registryRequest.getRegistryDataList()) {
            if (registryData.getKey() == null || registryData.getKey().trim().length() < 4 || registryData.getKey().trim().length() > 255) {
                return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Registry Key Invalid[4~255]");
            }
            if (registryData.getValue() == null || registryData.getValue().trim().length() < 4 || registryData.getValue().trim().length() > 255) {
                return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Registry Value Invalid[4~255]");
            }
        }

        // fill + add queue
        List<RpcRegistryData> registryDataList = new ArrayList<>();
        for (RpcAdminRegistryDataItem dataItem : registryRequest.getRegistryDataList()) {
            RpcRegistryData registryData = new RpcRegistryData();
            registryData.setEnv(registryRequest.getEnv());
            registryData.setKey(dataItem.getKey());
            registryData.setValue(dataItem.getValue());

            registryDataList.add(registryData);
        }
        removeQueue.addAll(registryDataList);

        return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.SUCCESS_CODE, null);
    }

    @Override
    public RpcAdminRegistryResponse discovery(RpcAdminRegistryRequest registryRequest) {

        // valid
        if (this.accessToken != null && this.accessToken.trim().length() > 0 && !this.accessToken.equals(registryRequest.getAccessToken())) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "AccessToken Invalid");
        }
        if (registryRequest.getEnv() == null || registryRequest.getEnv().trim().length() < 2 || registryRequest.getEnv().trim().length() > 255) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Env Invalid[2~255]");
        }
        if (registryRequest.getKeys() == null || registryRequest.getKeys().size() == 0) {
            return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "keys Invalid.");
        }
        for (String key : registryRequest.getKeys()) {
            if (key == null || key.trim().length() < 4 || key.trim().length() > 255) {
                return new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Key Invalid[4~255]");
            }
        }

        Map<String, TreeSet<String>> result = new HashMap<>();
        for (String key : registryRequest.getKeys()) {
            // key
            RpcRegistryData rpcRegistryData = new RpcRegistryData();
            rpcRegistryData.setEnv(registryRequest.getEnv());
            rpcRegistryData.setKey(key);

            // values
            TreeSet<String> dataList = new TreeSet<>();
            RpcRegistry fileRpcRegistry = getFileRegistryData(rpcRegistryData);
            if (fileRpcRegistry != null) {
                dataList.addAll(fileRpcRegistry.getDataList());
            }

            // fill
            result.put(key, dataList);
        }

        return new RpcAdminRegistryResponse(result);
    }

    @Override
    public DeferredResult<RpcAdminRegistryResponse> monitor(RpcAdminRegistryRequest registryRequest) {

        // init
        DeferredResult deferredResult = new DeferredResult(30 * 1000L, new RpcAdminRegistryResponse(RpcAdminRegistryResponse.SUCCESS_CODE, "Monitor timeout, no key updated."));

        // valid
        if (this.accessToken != null && this.accessToken.trim().length() > 0 && !this.accessToken.equals(registryRequest.getAccessToken())) {
            deferredResult.setResult(new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "AccessToken Invalid"));
            return deferredResult;
        }
        if (registryRequest.getEnv() == null || registryRequest.getEnv().trim().length() < 2 || registryRequest.getEnv().trim().length() > 255) {
            deferredResult.setResult(new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Env Invalid[2~255]"));
            return deferredResult;
        }
        if (registryRequest.getKeys() == null || registryRequest.getKeys().size() == 0) {
            deferredResult.setResult(new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "keys Invalid."));
            return deferredResult;
        }
        for (String key : registryRequest.getKeys()) {
            if (key == null || key.trim().length() < 4 || key.trim().length() > 255) {
                deferredResult.setResult(new RpcAdminRegistryResponse(RpcAdminRegistryResponse.FAIL_CODE, "Key Invalid[4~255]"));
                return deferredResult;
            }
        }

        // monitor by client
        for (String key : registryRequest.getKeys()) {
            String fileName = parseRegistryDataFileName(registryRequest.getEnv(), key);

            List<DeferredResult> deferredResultList = registryDeferredResultMap.computeIfAbsent(fileName, k -> new ArrayList<>());

            deferredResultList.add(deferredResult);
        }

        return deferredResult;
    }

    /**
     * update Registry And Message
     */
    private void checkRegistryDataAndSendMessage(RpcRegistryData rpcRegistryData) {
        // data json
        List<RpcRegistryData> rpcRegistryDataList = rpcRegistryDataMapper.findData(rpcRegistryData.getEnv(), rpcRegistryData.getKey());
        List<String> valueList = new ArrayList<>();
        if (rpcRegistryDataList != null && rpcRegistryDataList.size() > 0) {
            for (RpcRegistryData dataItem : rpcRegistryDataList) {
                valueList.add(dataItem.getValue());
            }
        }
        String dataJson = GsonTool.toJson(valueList);

        // update registry and message
        RpcRegistry rpcRegistry = rpcRegistryMapper.load(rpcRegistryData.getEnv(), rpcRegistryData.getKey());
        boolean needMessage = false;
        if (rpcRegistry == null) {
            rpcRegistry = new RpcRegistry();
            rpcRegistry.setEnv(rpcRegistryData.getEnv());
            rpcRegistry.setKey(rpcRegistryData.getKey());
            rpcRegistry.setData(dataJson);
            rpcRegistryMapper.add(rpcRegistry);
            needMessage = true;
        } else {

            // check status, locked and disabled not use
            if (rpcRegistry.getStatus() != 0) {
                return;
            }

            if (!rpcRegistry.getData().equals(dataJson)) {
                rpcRegistry.setData(dataJson);
                rpcRegistryMapper.update(rpcRegistry);
                needMessage = true;
            }
        }

        if (needMessage) {
            // sendRegistryDataUpdateMessage (registry update)
            sendRegistryDataUpdateMessage(rpcRegistry);
        }

    }

    @Override
    public void afterPropertiesSet() throws Exception {

        // valid
        if (registryDataFilePath == null || registryDataFilePath.trim().length() == 0) {
            throw new TripleRpcException("registryDataFilePath empty.");
        }

        /**
         * registry registry data      (client-num/10 s)
         */
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                while (!executorStoped) {
                    try {
                        RpcRegistryData rpcRegistryData = registryQueue.take();

                        // refresh or add
                        int ret = rpcRegistryDataMapper.refresh(rpcRegistryData);
                        if (ret == 0) {
                            rpcRegistryDataMapper.add(rpcRegistryData);
                        }

                        // valid file status
                        RpcRegistry fileRpcRegistry = getFileRegistryData(rpcRegistryData);
                        if (fileRpcRegistry == null) {
                            // go on
                        } else if (fileRpcRegistry.getStatus() != 0) {
                            continue;     // "Status limited."
                        } else if (fileRpcRegistry.getDataList().contains(rpcRegistryData.getValue())) {
                            continue;     // "Repeated limited."
                        }

                        // checkRegistryDataAndSendMessage
                        checkRegistryDataAndSendMessage(rpcRegistryData);
                    } catch (Exception e) {
                        if (!executorStoped) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }
            });
        }

        /**
         * remove registry data         (client-num/start-interval s)
         */
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                while (!executorStoped) {
                    try {
                        RpcRegistryData rpcRegistryData = removeQueue.take();

                        // delete
                        rpcRegistryDataMapper.deleteDataValue(rpcRegistryData.getEnv(), rpcRegistryData.getKey(), rpcRegistryData.getValue());

                        // valid file status
                        RpcRegistry fileRpcRegistry = getFileRegistryData(rpcRegistryData);
                        if (fileRpcRegistry == null) {
                            // go on
                        } else if (fileRpcRegistry.getStatus() != 0) {
                            continue;   // "Status limited."
                        } else if (!fileRpcRegistry.getDataList().contains(rpcRegistryData.getValue())) {
                            continue;   // "Repeated limited."
                        }

                        // checkRegistryDataAndSendMessage
                        checkRegistryDataAndSendMessage(rpcRegistryData);
                    } catch (Exception e) {
                        if (!executorStoped) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }
            });
        }

        /**
         * broadcase new one registry-data-file     (1/1s)
         *
         * clean old message   (1/10s)
         */
        executorService.execute(() -> {
            while (!executorStoped) {
                try {
                    // new message, filter readed
                    List<RpcRegistryMessage> messageList = rpcRegistryMessageMapper.findMessage(readedMessageIds);
                    if (messageList != null && messageList.size() > 0) {
                        for (RpcRegistryMessage message : messageList) {
                            readedMessageIds.add(message.getId());

                            if (message.getType() == 0) {   // from registry、add、update、deelete，ne need sync from db, only write

                                RpcRegistry rpcRegistry = GsonTool.fromJson(message.getData(), RpcRegistry.class);

                                // process data by status
                                if (rpcRegistry.getStatus() == 1) {
                                    // locked, not updated
                                } else if (rpcRegistry.getStatus() == 2) {
                                    // disabled, write empty
                                    rpcRegistry.setData(GsonTool.toJson(new ArrayList<String>()));
                                } else {
                                    // default, sync from db （aready sync before message, only write）
                                }

                                // sync file
                                setFileRegistryData(rpcRegistry);
                            }
                        }
                    }

                    // clean old message;
                    if ((System.currentTimeMillis() / 1000) % registryBeatTime == 0) {
                        rpcRegistryMessageMapper.cleanMessage(registryBeatTime);
                        readedMessageIds.clear();
                    }
                } catch (Exception e) {
                    if (!executorStoped) {
                        log.error(e.getMessage(), e);
                    }
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    if (!executorStoped) {
                        log.error(e.getMessage(), e);
                    }
                }
            }
        });

        /**
         *  clean old registry-data     (1/10s)
         *
         *  sync total registry-data db + file      (1+N/10s)
         *
         *  clean old registry-data file
         */
        executorService.execute(() -> {
            while (!executorStoped) {

                // align to beattime
                try {
                    long sleepSecond = registryBeatTime - (System.currentTimeMillis() / 1000) % registryBeatTime;
                    if (sleepSecond > 0 && sleepSecond < registryBeatTime) {
                        TimeUnit.SECONDS.sleep(sleepSecond);
                    }
                } catch (Exception e) {
                    if (!executorStoped) {
                        log.error(e.getMessage(), e);
                    }
                }

                try {
                    // clean old registry-data in db
                    rpcRegistryDataMapper.cleanData(registryBeatTime * 3);

                    // sync registry-data, db + file
                    int offset = 0;
                    int pagesize = 1000;
                    List<String> registryDataFileList = new ArrayList<>();

                    List<RpcRegistry> registryList = rpcRegistryMapper.pageList(offset, pagesize, null, null);
                    while (registryList != null && registryList.size() > 0) {

                        for (RpcRegistry registryItem : registryList) {

                            // process data by status
                            if (registryItem.getStatus() == 1) {
                                // locked, not updated
                            } else if (registryItem.getStatus() == 2) {
                                // disabled, write empty
                                String dataJson = GsonTool.toJson(new ArrayList<String>());
                                registryItem.setData(dataJson);
                            } else {
                                // default, sync from db
                                List<RpcRegistryData> rpcRegistryDataList = rpcRegistryDataMapper.findData(registryItem.getEnv(), registryItem.getKey());
                                List<String> valueList = new ArrayList<String>();
                                if (rpcRegistryDataList != null && rpcRegistryDataList.size() > 0) {
                                    for (RpcRegistryData dataItem : rpcRegistryDataList) {
                                        valueList.add(dataItem.getValue());
                                    }
                                }
                                String dataJson = GsonTool.toJson(valueList);

                                // check update, sync db
                                if (!registryItem.getData().equals(dataJson)) {
                                    registryItem.setData(dataJson);
                                    rpcRegistryMapper.update(registryItem);
                                }
                            }

                            // sync file
                            String registryDataFile = setFileRegistryData(registryItem);

                            // collect registryDataFile
                            registryDataFileList.add(registryDataFile);
                        }


                        offset += 1000;
                        registryList = rpcRegistryMapper.pageList(offset, pagesize, null, null);
                    }

                    // clean old registry-data file
                    cleanFileRegistryData(registryDataFileList);

                } catch (Exception e) {
                    if (!executorStoped) {
                        log.error(e.getMessage(), e);
                    }
                }
                try {
                    TimeUnit.SECONDS.sleep(registryBeatTime);
                } catch (Exception e) {
                    if (!executorStoped) {
                        log.error(e.getMessage(), e);
                    }
                }
            }
        });


    }

    @Override
    public void destroy() throws Exception {
        executorStoped = true;
        executorService.shutdownNow();
    }


    // ------------------------ file opt ------------------------

    // get
    public RpcRegistry getFileRegistryData(RpcRegistryData rpcRegistryData) {

        // fileName
        String fileName = parseRegistryDataFileName(rpcRegistryData.getEnv(), rpcRegistryData.getKey());

        // read
        Properties prop = PropUtil.loadProp(fileName);
        if (prop != null) {
            RpcRegistry fileRpcRegistry = new RpcRegistry();
            fileRpcRegistry.setData(prop.getProperty("data"));
            fileRpcRegistry.setStatus(Integer.valueOf(prop.getProperty("status")));
            fileRpcRegistry.setDataList(GsonTool.fromJson(fileRpcRegistry.getData(), List.class));
            return fileRpcRegistry;
        }
        return null;
    }

    private String parseRegistryDataFileName(String env, String key) {
        // fileName
        String fileName = registryDataFilePath
                .concat(File.separator).concat(env)
                .concat(File.separator).concat(key)
                .concat(".properties");
        return fileName;
    }

    // set
    public String setFileRegistryData(RpcRegistry rpcRegistry) {

        // fileName
        String fileName = parseRegistryDataFileName(rpcRegistry.getEnv(), rpcRegistry.getKey());

        // valid repeat update
        Properties existProp = PropUtil.loadProp(fileName);
        if (existProp != null
                && existProp.getProperty("data").equals(rpcRegistry.getData())
                && existProp.getProperty("status").equals(String.valueOf(rpcRegistry.getStatus()))
        ) {
            return new File(fileName).getPath();
        }

        // write
        Properties prop = new Properties();
        prop.setProperty("data", rpcRegistry.getData());
        prop.setProperty("status", String.valueOf(rpcRegistry.getStatus()));

        PropUtil.writeProp(prop, fileName);

        log.info(">>>>>>>>>>> setFileRegistryData: env={}, key={}, data={}"
                , rpcRegistry.getEnv(), rpcRegistry.getKey(), rpcRegistry.getData());


        // brocast monitor client
        List<DeferredResult> deferredResultList = registryDeferredResultMap.get(fileName);
        if (deferredResultList != null) {
            registryDeferredResultMap.remove(fileName);
            for (DeferredResult deferredResult : deferredResultList) {
                deferredResult.setResult(new ReturnT<>(ReturnT.SUCCESS_CODE, "Monitor key update."));
            }
        }

        return new File(fileName).getPath();
    }

    // clean
    public void cleanFileRegistryData(List<String> registryDataFileList) {
        filterChildPath(new File(registryDataFilePath), registryDataFileList);
    }

    public void filterChildPath(File parentPath, final List<String> registryDataFileList) {
        if (!parentPath.exists() || parentPath.list() == null || parentPath.list().length == 0) {
            return;
        }
        File[] childFileList = parentPath.listFiles();
        for (File childFile : childFileList) {
            if (childFile.isFile() && !registryDataFileList.contains(childFile.getPath())) {
                childFile.delete();

                log.info(">>>>>>>>>>>  cleanFileRegistryData, RegistryData Path={}", childFile.getPath());
            }
            if (childFile.isDirectory()) {
                if (parentPath.listFiles() != null && parentPath.listFiles().length > 0) {
                    filterChildPath(childFile, registryDataFileList);
                } else {
                    childFile.delete();
                }

            }
        }

    }

}

